home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / golded / api / spell / main.c next >
C/C++ Source or Header  |  1995-02-17  |  20KB  |  702 lines

  1. /* -----------------------------------------------------------------------------
  2.  
  3.  GoldED API client example code, ©1995 Dietmar Eilert. Dice:
  4.  
  5.  dcc main.c -// -proto -mi -r -2.0 -l reqtoolss.lib -o ram:spell
  6.  
  7.  Note: Compiling this code requires reqtools includes & reqtools linker
  8.        libraries. The ReqTools library is © Nico François.
  9.  
  10.  The following code adds spell checking capabilities to GoldED. It is  based  on
  11.  the  ISpell package and the ISpell dictionary (available on Fish disks). ISpell
  12.  has to be installed properly (within a valid command path) before you  can  use
  13.  this  API  client.  This example uses synchronous ARexx communication: Requests
  14.  are PutMsg()'ed to GoldED's port, followed by a WaitPort() to get the  editor's
  15.  response.  This  works  fine since we need no ARexx communication after the API
  16.  link has been established. If there were ARexx communication with GoldED  AFTER
  17.  the link has been established (i.e. after sending the 'API PORT=...' command to
  18.  register with GoldED), we would have  to  use  an  asynchronous  design  beeing
  19.  capable  of  answering  incoming  API  messages while waiting for completion of
  20.  ARexx requests sent to GoldED.
  21.  
  22.   -------------------------------------------------------------------------------
  23. */
  24.  
  25. /// "includes"
  26.  
  27. #define Prototype extern
  28.  
  29. #include <exec/exec.h>
  30. #include <string.h>
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33. #include <dos/dos.h>
  34. #include <dos/dostags.h>
  35. #include <dos/rdargs.h>
  36. #include <intuition/intuition.h>
  37. #include <utility/tagitem.h>
  38. #include <libraries/gadtools.h>
  39. #include <rexx/errors.h>
  40. #include <rexx/rxslib.h>
  41. #include <clib/exec_protos.h>
  42. #include <clib/dos_protos.h>
  43. #include <clib/graphics_protos.h>
  44. #include <clib/intuition_protos.h>
  45. #include <clib/diskfont_protos.h>
  46. #include <clib/rexxsyslib_protos.h>
  47. #include <clib/gadtools_protos.h>
  48. #include <clib/reqtools_protos.h>
  49. #include <clib/alib_protos.h>
  50. #include <devices/audio.h>
  51. #include "golded:api/include/golded.h"
  52.  
  53. Prototype void   main(int, char **);
  54. Prototype BOOL   InitISpell(void);
  55. Prototype void   HandleAPI(BOOL, BOOL, char *);
  56. Prototype ULONG *SendRexxCommand(char *, char *, struct MsgPort *, char *);
  57. Prototype void   CheckWord(struct APIMessage *, struct MsgPort *, BOOL, BOOL);
  58. Prototype void   Beep(void);
  59. Prototype UWORD  ComputeX(struct TextFont *, UWORD);
  60. Prototype UWORD  ComputeY(struct TextFont *, UWORD);
  61. Prototype char  *ShowSpell(char *, struct List *);
  62. Prototype struct Node *SearchNode(struct List *, UWORD);
  63.  
  64. struct Library *ReqToolsBase;
  65.  
  66. ///
  67. /// "main"
  68.  
  69. void
  70. main(int argc, char **argv)
  71. {
  72.     const char *version = "$VER: spell 1.5 (" __COMMODORE_DATE__ ")";
  73.  
  74.     struct RDArgs *rdArgs;
  75.  
  76.     ULONG args[] = { 0, 0, 0 };
  77.  
  78.     if (rdArgs = ReadArgs("ASK/S,NOFLASH/S,HOST/K/A", args, NULL)) {
  79.  
  80.         if (ReqToolsBase = (struct ReqToolsBase *)OpenLibrary("reqtools.library", 0)) {
  81.  
  82.             if (InitISpell())
  83.                 HandleAPI(args[0], args[1], (char *)args[2]);
  84.  
  85.             CloseLibrary(ReqToolsBase);
  86.         }
  87.  
  88.         FreeArgs(rdArgs);
  89.     }
  90.     else
  91.         puts("syntax error: SPELL ASK/S,HOST/K/A");
  92.  
  93.     exit(0);
  94. }
  95.  
  96. ///
  97. /// "init"
  98.  
  99. /* -------------------------------- IntiISpell ---------------------------------
  100.  
  101.  Prepare ISpell. Return TRUE if successful.
  102.  
  103. */
  104.  
  105. BOOL
  106. InitISpell()
  107. {
  108.     if (FindPort("IRexxSpell"))
  109.  
  110.         return(TRUE);
  111.  
  112.     else {
  113.  
  114.         struct TagItem tags[] = {SYS_Output, NULL, SYS_Input, NULL, SYS_Asynch, TRUE, TAG_DONE};
  115.  
  116.         if (SystemTagList("ispell -r", tags) != -1) {
  117.  
  118.             UWORD n;
  119.  
  120.             for (n = 0; n < 100; n++, Delay(10))
  121.                 if (FindPort("IRexxSpell"))
  122.                     return(TRUE);
  123.         }
  124.     }
  125.  
  126.     puts("ISpell not available");
  127.  
  128.     return(FALSE);
  129. }
  130.  
  131. ///
  132. /// "api management"
  133.  
  134. /* --------------------------------- HandleAPI ---------------------------------
  135.  
  136.  Register with GoldED & handle incoming API messages.
  137.  
  138. */
  139.  
  140. void
  141. HandleAPI(ask, noFlash, host)
  142.  
  143. BOOL ask, noFlash;
  144. char *host;
  145. {
  146.     struct MsgPort *rexxPort, *apiPort;
  147.  
  148.     if (apiPort = CreateMsgPort()) {
  149.  
  150.         if (rexxPort = CreateMsgPort()) {
  151.  
  152.             char  command[255];
  153.             ULONG *result;
  154.  
  155.             sprintf(command, "API PORT=%ld CLASS=%ld", apiPort, API_CLASS_ROOT | API_CLASS_KEY);
  156.  
  157.             if (result = SendRexxCommand(host, command, rexxPort, NULL)) {
  158.  
  159.                 if (*result == RC_OK) {
  160.  
  161.                     BOOL active = TRUE;
  162.  
  163.                     do {
  164.  
  165.                         struct APIMessage *apiMsg, *nextMsg;
  166.  
  167.                         while (!(apiMsg = (struct APIMessage *)GetMsg(apiPort)))
  168.                             WaitPort(apiPort);
  169.  
  170.                         do {
  171.  
  172.                             for (nextMsg = apiMsg; nextMsg; nextMsg = nextMsg->api_Next) {
  173.  
  174.                                 if (nextMsg->api_State == API_STATE_NOTIFY) {
  175.  
  176.                                     switch (nextMsg->api_Class) {
  177.  
  178.                                         case API_CLASS_ROOT:
  179.  
  180.                                             switch (nextMsg->api_Action) {
  181.  
  182.                                                 case API_ACTION_DIE:
  183.  
  184.                                                     active = FALSE;
  185.                                                     break;
  186.  
  187.                                                 case API_ACTION_INTRODUCE:
  188.  
  189.                                                     static struct TagItem tags[] = {
  190.  
  191.                                                         API_Client_Name,      "spell",
  192.                                                         API_Client_Copyright, "©1995 Dietmar Eilert",
  193.                                                         API_Client_Purpose,   "Adds online spell checking",
  194.                                                         TAG_DONE
  195.                                                     };
  196.  
  197.                                                     nextMsg->api_Data = tags;
  198.                                                     break;
  199.  
  200.                                                 default:
  201.  
  202.                                                     nextMsg->api_Error = API_ERROR_UNKNOWN;
  203.                                             }
  204.  
  205.                                             break;
  206.  
  207.                                         case API_CLASS_KEY:
  208.  
  209.                                             switch (nextMsg->api_Action) {
  210.  
  211.                                                 case API_ACTION_VANILLAKEY:
  212.  
  213.                                                     // checks are performed after white space characters:
  214.  
  215.                                                     if ((UBYTE)nextMsg->api_Data < 'A')
  216.                                                         CheckWord(nextMsg, rexxPort, ask, noFlash);
  217.  
  218.                                                     break;
  219.  
  220.                                                 default:
  221.  
  222.                                                     nextMsg->api_Error = API_ERROR_UNKNOWN;
  223.                                             }
  224.                                             break;
  225.  
  226.                                         default:
  227.  
  228.                                             nextMsg->api_Error = API_ERROR_UNKNOWN;
  229.                                     }
  230.                                 }
  231.                             }
  232.  
  233.                             ReplyMsg((struct Message *)apiMsg);
  234.  
  235.                         } while (apiMsg = (struct APIMessage *)GetMsg(apiPort));
  236.  
  237.                     } while (active);
  238.                 }
  239.             }
  240.  
  241.             SendRexxCommand("IRexxSpell", "EXIT", rexxPort, NULL);
  242.  
  243.             DeleteMsgPort(rexxPort);
  244.         }
  245.  
  246.         DeleteMsgPort(apiPort);
  247.     }
  248. }
  249.  
  250.  
  251. ///
  252. /// "check word"
  253.  
  254. /* --------------------------------- CheckWord ---------------------------------
  255.  
  256.  Check word. This function is called BEFORE  GoldED  actually  inserts  a  white
  257.  space  character  (space, colon, ...) at the current cursor position. We assume
  258.  the current position to  be  a  white  space  character  to  guarantee  correct
  259.  behavior if the user inserts a character into a word. If ISpell complains about
  260.  a word we will either issue a beep (ask = FALSE) or additionally display a list
  261.  of suggestions (<ask> = TRUE).
  262.  
  263.  Excerpt taken from man/ispell.1: If the word is  not  in  the  dictionary,  but
  264.  there  are  near  misses,  then the result line contains an '&', a space, and a
  265.  list of the near misses separated by spaces.
  266.  
  267. */
  268.  
  269. void
  270. CheckWord(apiMsg, rexxPort, ask, noFlash)
  271.  
  272. struct APIMessage *apiMsg;
  273. struct MsgPort    *rexxPort;
  274. BOOL   ask, noFlash;
  275. {
  276.     static UBYTE buffer[4096];
  277.  
  278.     struct EditConfig *config;
  279.     UBYTE             *next;
  280.     UWORD              column;
  281.  
  282.     config = apiMsg->api_Config;
  283.     column = config->Column;
  284.  
  285.     // create copy of current line
  286.  
  287.     movmem(config->CurrentBuffer, buffer, config->CurrentLen);
  288.  
  289.     // cursor placed directly after word ?
  290.  
  291.     if (column && (buffer[--column] > '@')) {
  292.  
  293.         static char word[1000], result[1000];
  294.  
  295.         UWORD wordLen;
  296.  
  297.         // find beginning of word
  298.  
  299.         for (next = buffer + column, wordLen = 1; column && (*(next - 1) > '@'); --column, --next)
  300.             ++wordLen;
  301.  
  302.         // create ISpell command line
  303.  
  304.         movmem(next, word, wordLen);
  305.  
  306.         word[wordLen] = 0;
  307.  
  308.         strins(word, "QUICKCHECK ");
  309.  
  310.         // make ISpell check word
  311.  
  312.         if (SendRexxCommand("IRexxSpell", word, rexxPort, result)) {
  313.  
  314.             if (stricmp(result, "ok")) {
  315.  
  316.                 if (noFlash)
  317.                     Beep();
  318.                 else
  319.                     DisplayBeep(NULL);
  320.  
  321.                 // send additional CHECK command to get further information
  322.  
  323.                 if (ask && SendRexxCommand("IRexxSpell", word + 5, rexxPort, result)) {
  324.  
  325.                     if (*result == '&') {
  326.  
  327.                         // translate ISpell result string (suggestions) into exec list
  328.  
  329.                         struct List list;
  330.                         struct Node *node,  *nextNode;
  331.                         char        *start, *end, *selection;
  332.  
  333.                         for (NewList(&list), end = result + 2; *end;) {
  334.  
  335.                             for (start = end; *end && (*end != 32); ++end);
  336.  
  337.                             if (node = (struct Node *)AllocVec(sizeof(struct Node), MEMF_ANY | MEMF_CLEAR)) {
  338.  
  339.                                 node->ln_Name = start;
  340.                                 AddTail(&list, node);
  341.                             }
  342.  
  343.                             if (*end)
  344.                                 *end++ = 0;
  345.                         }
  346.  
  347.                         // ask for user selection from list, insert user selection into text
  348.  
  349.                         if (selection = ShowSpell(apiMsg->api_Global->F_ScrnName, &list)) {
  350.  
  351.                             static struct APIModifyRequest modifyRequest;
  352.  
  353.                             UWORD newLen = strlen(selection);
  354.  
  355.                             if (newLen != wordLen)
  356.                                 movmem(next + wordLen, next + newLen, config->CurrentLen - (next - buffer));
  357.  
  358.                             movmem(selection, next, newLen);
  359.  
  360.                             // make GoldED change the current line
  361.  
  362.                             modifyRequest.mr_Next   = NULL;
  363.                             modifyRequest.mr_Line   = config->Line;
  364.                             modifyRequest.mr_Column = config->Column     + (newLen - wordLen);
  365.                             modifyRequest.mr_Size   = config->CurrentLen + (newLen - wordLen);
  366.                             modifyRequest.mr_Data   = buffer;
  367.  
  368.                             apiMsg->api_Modify = &modifyRequest;
  369.                         }
  370.  
  371.                         // free list of suggestions
  372.  
  373.                         for (node = list.lh_Head; nextNode = node->ln_Succ; node = nextNode)
  374.                             FreeVec(node);
  375.                     }
  376.                 }
  377.             }
  378.         }
  379.     }
  380. }
  381.  
  382. ///
  383. /// "gui"
  384.  
  385. /* --------------------------------- ShowSpell ---------------------------------
  386.  
  387.  Show ISpell suggestions. Return user selection or NULL.
  388.  
  389. */
  390.  
  391. char *
  392. ShowSpell(screen, list)
  393.  
  394. char        *screen;
  395. struct List *list;
  396. {
  397.     char *result = NULL;
  398.  
  399.     struct Screen *scr;
  400.  
  401.     if (scr = LockPubScreen(screen)) {
  402.  
  403.         struct TextFont *font;
  404.  
  405.         if (font = OpenDiskFont(scr->Font)) {
  406.  
  407.             APTR visualInfo;
  408.  
  409.             if (visualInfo = GetVisualInfoA(scr, NULL )) {
  410.  
  411.                 struct Gadget *context, *glist, *gad;
  412.  
  413.                 if (context = CreateContext(&glist)) {
  414.  
  415.                     UWORD  ww, wh, offX, offY;
  416.                     LONG   displayWidth, displayHeight;
  417.  
  418.                     struct Window *win;
  419.  
  420.                     offX  = scr->WBorLeft;
  421.                     offY  = scr->RastPort.TxHeight + scr->WBorTop + 1;
  422.  
  423.                     struct NewGadget newGad = {
  424.                     
  425.                         offX,
  426.                         offY,
  427.                         ComputeX(font, 221),
  428.                         ComputeY(font, 156),
  429.                         NULL, scr->Font, 0, 0, visualInfo, 0
  430.                     };
  431.  
  432.                     gad = CreateGadget(LISTVIEW_KIND, context, &newGad, GTLV_ShowSelected, NULL, GTLV_Labels, list, GTLV_Selected, 0, TAG_DONE);
  433.  
  434.                     rtGetVScreenSize(scr, &displayWidth, &displayHeight);
  435.  
  436.                     ww = ComputeX(font, 221);
  437.                     wh = ComputeY(font, 156);
  438.  
  439.                     if (win = OpenWindowTags( NULL,
  440.  
  441.                         WA_Left,      ((displayWidth  - ww)>>1) - scr->ViewPort.DxOffset,
  442.                         WA_Top,       ((displayHeight - wh)>>1) - scr->ViewPort.DyOffset,
  443.                         WA_Width,     ww + offX + scr->WBorRight,
  444.                         WA_Height,    wh + offY + scr->WBorBottom,
  445.                         WA_IDCMP,     LISTVIEWIDCMP | IDCMP_CLOSEWINDOW | IDCMP_VANILLAKEY  | IDCMP_RAWKEY,
  446.                         WA_Flags,     WFLG_DRAGBAR  | WFLG_DEPTHGADGET  | WFLG_CLOSEGADGET  | WFLG_SMART_REFRESH | WFLG_ACTIVATE,
  447.                         WA_Gadgets,   glist,
  448.                         WA_Title,     "ISpell",
  449.                         WA_PubScreen, scr,
  450.                         TAG_DONE 
  451.  
  452.                      )) {
  453.  
  454.                         ULONG class;
  455.                         UWORD active;
  456.  
  457.                         active = 0;
  458.                         result = list->lh_Head->ln_Name;
  459.  
  460.                         struct IntuiMessage *msg;
  461.                         struct Node         *node;
  462.  
  463.                         GT_RefreshWindow(win, NULL);
  464.  
  465.  
  466.                         do {
  467.  
  468.                             while (!(msg = GT_GetIMsg(win->UserPort)))
  469.                                 WaitPort(win->UserPort);
  470.  
  471.                             switch (class = msg->Class) {
  472.  
  473.                                 case RAWKEY:
  474.  
  475.                                     WORD step = (msg->Code == CURSORUP) ? -1 : 1;
  476.  
  477.                                     if (node = SearchNode(list, active + step)) {
  478.  
  479.                                         active += step;
  480.                                         result  = node->ln_Name;
  481.  
  482.                                         GT_SetGadgetAttrs(gad, win, NULL, GTLV_Selected, active, GTLV_MakeVisible, active, TAG_DONE);
  483.                                     }
  484.  
  485.                                     break;
  486.  
  487.                                 case IDCMP_VANILLAKEY:
  488.  
  489.                                     if (msg->Code == 27)
  490.                                         class  = CLOSEWINDOW;
  491.  
  492.                                     if (msg->Code == 13)
  493.                                         class = IDCMP_GADGETUP;
  494.  
  495.                                     break;
  496.  
  497.                                 case IDCMP_GADGETUP:
  498.  
  499.                                     result = SearchNode(list, active = msg->Code)->ln_Name;
  500.                                     break;
  501.                             }
  502.  
  503.                             GT_ReplyIMsg(msg);
  504.  
  505.                         } while ((class & (GADGETUP | CLOSEWINDOW)) == NULL);
  506.  
  507.                         if (class == CLOSEWINDOW)
  508.                             result = NULL;
  509.  
  510.                         CloseWindow(win);
  511.                     }
  512.                 }
  513.                 FreeVisualInfo(visualInfo);
  514.             }
  515.         }
  516.         UnlockPubScreen(screen, scr);
  517.     }
  518.  
  519.     return(result);
  520. }
  521.  
  522. /* --------------------------------- ComputeX ----------------------------------
  523.  
  524.  Resize element of width <value> according to current font
  525.  
  526. */
  527.  
  528. UWORD 
  529. ComputeX(font, value)
  530.  
  531. UWORD  value;
  532. struct TextFont *font;
  533. {
  534.     return((font->tf_XSize * value) / 8);
  535. }          
  536.  
  537. /* --------------------------------- ComputeY ----------------------------------
  538.  
  539.  Resize element of height <value> according to current font
  540.  
  541. */
  542.  
  543. UWORD 
  544. ComputeY(font, value)
  545.  
  546. UWORD  value;
  547. struct TextFont *font;
  548. {
  549.     return((font->tf_YSize * value) / 8);
  550. }
  551.  
  552. ///
  553. /// "misc"
  554.  
  555. /* ---------------------------------- SearchNode --------------------------------
  556.  
  557.  Return pointer to node if ordinal number (0, ...) is known.
  558.  
  559. */
  560.  
  561. struct Node *
  562. SearchNode(list, ordinal)
  563.  
  564. struct List *list;
  565. UWORD  ordinal;
  566. {
  567.     struct Node *node;
  568.  
  569.     for (node = list->lh_Head; node->ln_Succ; --ordinal, node = node->ln_Succ)
  570.         if (!ordinal)
  571.             return(node);
  572.  
  573.     return(NULL);
  574. }
  575.  
  576. /* ----------------------------------- Beep ------------------------------------
  577.  
  578.  Short audible beep
  579.  
  580. */
  581.  
  582. void
  583. Beep()
  584. {
  585.     struct IOAudio *audioIO;
  586.  
  587.     if (audioIO = (struct IOAudio *)AllocVec(sizeof(struct IOAudio), MEMF_PUBLIC | MEMF_CLEAR)) {
  588.  
  589.         struct MsgPort *audioMP;
  590.  
  591.         if (audioMP = CreateMsgPort()) {
  592.  
  593.             UBYTE whichannel[] = { 1, 2, 4, 8 };
  594.  
  595.             audioIO->ioa_Request.io_Message.mn_ReplyPort   = audioMP;
  596.             audioIO->ioa_Request.io_Message.mn_Node.ln_Pri = 0;
  597.             audioIO->ioa_Request.io_Command                = ADCMD_ALLOCATE;
  598.             audioIO->ioa_Request.io_Flags                  = ADIOF_NOWAIT;
  599.             audioIO->ioa_AllocKey                          = 0;
  600.             audioIO->ioa_Data                              = whichannel;
  601.             audioIO->ioa_Length                            = sizeof(whichannel);
  602.  
  603.             if (!OpenDevice("audio.device", 0, (struct IORequest *)audioIO, 0)) {
  604.  
  605.                 __chip const static UBYTE waveptr[2] = {127, -127};
  606.  
  607.                 audioIO->ioa_Request.io_Message.mn_ReplyPort = audioMP;
  608.                 audioIO->ioa_Request.io_Command              = CMD_WRITE;
  609.                 audioIO->ioa_Request.io_Flags                = ADIOF_PERVOL;
  610.  
  611.                 audioIO->ioa_Data   = waveptr;
  612.                 audioIO->ioa_Length = 2;
  613.                 audioIO->ioa_Period = 1015;
  614.                 audioIO->ioa_Volume = 32;
  615.                 audioIO->ioa_Cycles = 60; // 44;
  616.  
  617.                 BeginIO((struct IORequest *)audioIO );
  618.  
  619.                 WaitPort(audioMP);
  620.                 GetMsg  (audioMP);
  621.  
  622.                 CloseDevice((struct IORequest *)audioIO);
  623.             }
  624.  
  625.             DeleteMsgPort(audioMP);
  626.         }
  627.  
  628.         FreeVec(audioIO);
  629.     }
  630. }
  631.  
  632. ///
  633. /// "arexx"
  634.  
  635. /* ---------------------------------- SendRexxCommand -------------------------
  636.  
  637.  Send ARexx message & wait for answer. Return pointer to result or NULL.
  638.  
  639. */
  640.  
  641. ULONG *
  642. SendRexxCommand(port, cmd, replyPort, buffer)
  643.  
  644. char   *cmd, *port, *buffer;
  645. struct MsgPort *replyPort;
  646. {
  647.     struct MsgPort *rexxport;
  648.  
  649.     Forbid();
  650.  
  651.     if (rexxport = FindPort(port)) {
  652.  
  653.         struct RexxMsg *rexxMsg, *answer;
  654.  
  655.         if (rexxMsg = CreateRexxMsg(replyPort, NULL, NULL)) {
  656.  
  657.             if (rexxMsg->rm_Args[0] = CreateArgstring(cmd, strlen(cmd))) {
  658.  
  659.                 static ULONG result;
  660.  
  661.                 rexxMsg->rm_Action = RXCOMM | RXFF_RESULT;
  662.  
  663.                 PutMsg(rexxport, &rexxMsg->rm_Node);
  664.  
  665.                 do {
  666.                     
  667.                     WaitPort(replyPort);
  668.  
  669.                     if (answer = (struct RexxMsg *)GetMsg(replyPort))
  670.                         result = answer->rm_Result1;
  671.  
  672.                 } while (!answer);
  673.  
  674.                 Permit();
  675.  
  676.                 if (answer->rm_Result1 == RC_OK) {
  677.  
  678.                     if (answer->rm_Result2) {
  679.  
  680.                         if (buffer)
  681.                             strcpy(buffer, (char *)answer->rm_Result2);
  682.  
  683.                         DeleteArgstring((char *)answer->rm_Result2);
  684.                     }
  685.                 }
  686.  
  687.                 DeleteArgstring((char *)ARG0(answer));
  688.  
  689.                 DeleteRexxMsg(answer);
  690.  
  691.                 return(&result);
  692.             }
  693.         }
  694.     }
  695.  
  696.     Permit();
  697.  
  698.     return(NULL);
  699. }
  700.  
  701. ///
  702.